<?

class model_dbm {
    
    public static $qtde_consultas = 0;
    
    protected $host;
    protected $user;
    protected $password;
    protected $db;

    protected $rows = '*';
    protected $row;
    protected $query;
    protected $regex_pattern;
    protected $fetch_assoc_ja_executado_para_nova_query = false;
    protected $vet_provisorio;
    protected $resp_multiple_rows;
    protected $resp_single_row;
    protected $columns_names_type;
    protected $usar_limite = false;
    protected $string_query;
    protected $select_all_columns = false;
    protected $fetch_row = false;
    protected $resource = NULL;
    protected $prefixo_todas_tabelas = NULL;
    protected $logar_acoes = false;
    protected $usuario_logar_acoes = NULL;
    protected $tipo_acoes_logar = array('UPDATE', 'DELETE');

    public function __construct($vet_conexao = NULL) {
        //echo 'nova instância Dbm; ';
        if ($vet_conexao != NULL) {
            $this->connect($vet_conexao);
        }
    }

    public function connect($vet_conexao = NULL) {
        $this->host = $vet_conexao[0];
        $this->user = $vet_conexao[1];
        $this->password = $vet_conexao[2];
        $this->db = $vet_conexao[3];
        
        if (!$this->resource = @mysql_connect($this->host, $this->user, $this->password, TRUE)) {
            throw new DBException("Erro mysql_connect(): [" . mysql_errno() . "] " . mysql_error());
        }
        //var_dump($this->conexao);
        if (!$banco = @mysql_select_db($this->db, $this->resource)) {
            throw new DBException("Erro mysql_select_db(): " . mysql_error());
        }
    }

    public function logarAcoes($logar, $usuario) {
        $this->usuario_logar_acoes = $usuario;
        $this->logar_acoes = $logar;
    }

    /**
     *
     * qcv - Query Columns Values
     * @return $this->cv();
     *
     */
    public function qcv($string_query, $columns_names) {
        $this->q($string_query);
        return $this->cv($columns_names);
    }

    // alias para query():
    public function q($string_query = NULL) {
        return $this->query($string_query);
    }
    

    public function query($string_query = NULL) {
        //echo "BANCO: " . $this->db . " -- $string_query <br>";
        self::$qtde_consultas++;
        
        $this->string_query = $string_query;
        $this->logarAcao();
        
        if ($this->rows == '1' && $this->usar_limite == true) {
            // tirando o ; do fim da string e adicionando o limite:
            $this->string_query = ereg_replace('(;)$', '', rtrim($this->string_query));
            $this->string_query.= " LIMIT 0, 1;";
        }
        if (!$this->query = @mysql_query($this->string_query, $this->resource)) {
            
            
            
            if(mysql_errno() == 1451) {
                throw new DBM_ForeignKeyConstraintException();
            }
            
            if(mysql_errno() == 1062) {
                throw new DBM_DuplicateKeyException();
            }
            
            // outro tipo de erro:
            throw new DBException("Erro mysql_query(): " . mysql_error() . ", consulta: '" . $this->string_query . "';");
            
        }
        $this->fetch_assoc_ja_executado_para_nova_query = false;
    }
    
    
    protected function logarAcao() {
        if ($this->logar_acoes) {
            if (ereg("^(UPDATE|DELETE|INSERT)", $string_query)) {
                $this->q("insert INTO log_atividades (id, id_usuario, atividade, dt)
				VALUES (NULL, " . $this->usuario_logar_acoes . ", '" . addslashes($this->string_query) . "', NOW());");
            }
        }
    }

    public function usarUtf8() {
        // ainda não possui funcionalidade para voltar ao charset de conexão default.
        $sql = "SET CHARACTER SET utf8;";
        if (!mysql_query($sql)) {
            throw new DBException("Erro ao mudar o Character Set. " . mysql_error() . " sql: " . $sql);
        }
    }

    public function useSqlLimit($use) {
        // true ou false
        $this->usar_limite = $use;
    }

    public function reloadLastQuery() {
        // chamar esta função sempre que precisar refazer a última consulta efetuada com 'query()', e posteriormente carregar os resultados com 'getColumnsValues()';
        $this->query($this->string_query);
    }

    public function showQuery() {
        return $this->string_query;
    }

    public function setRows($rows) {
        // '1' para apenas uma linha de retorno, e '*' para várias linhas de retorno.
        if (!ereg('^(1|\*)$', $rows)) {
            throw new DBException("Função '" . __FUNCTION__ . "' só aceita como valores com o seguinte padrão: ^(1|\*)$, valor recebido: '" . $rows . "';");
        }
        $this->rows = $rows;
        $this->fetch_assoc_ja_executado_para_nova_query = false;
    }

    // alias para getColumnsValues():
    public function cv($columns_names, $fetch_row = false) {
        return $this->getColumnsValues($columns_names, $fetch_row);
    }

    public function getColumnsValues($columns_names, $fetch_row = false) {
        // $fetch_row = mysql_ fetch_row() (colunas como índice numérico no vetor);
        $this->fetch_row = $fetch_row;

        // esta função não aceita colunas que possuem vírgula no nome, pois a vírgula é usada para separar os nomes de colunas.
        if (is_string($columns_names)) {
            if (!preg_match('/^([^, ]+(,[^, ]+)*|\*)$/', $columns_names)) {
                throw new DBException("Separe as colunas entre vírgulas sem espaço, ou use '*', exemplos: 'cola,colb,colc', '*', 'coluna'; valor recebido: '" . $columns_names . "';");
            }
            $colunas = explode(',', $columns_names);
            if (sizeof($colunas) == 1) {
                $this->columns_names = $colunas[0];
            } else {
                $this->columns_names = $colunas;
            }
        }
        //var_dump($this->columns_names);
        /*
          $columnsNames = string ou array.
          obrigatoriamente usar string caso seja apenas uma coluna;
          obrigatoriamente usar array caso sejam 2 ou mais colunas;
         */
        if (is_string($this->columns_names)) {
            $this->columns_names_type = 'string';
            $this->verificaNome('column', $this->columns_names);
        } else if (is_array($this->columns_names)) {
            $this->columns_names_type = 'array';
            foreach ($this->columns_names as $v) {
                
                    $this->verificaNome('column', $v);
                
            }
        } else {
            //echo "nenhum tipo";
        }
        //echo "columns_names_type = " . $this->columns_names_type;
        if ($this->columns_names == '*') {
            // selecionar todas as colunas.
            $this->select_all_columns = true;
        } else {
            $this->select_all_columns = false;
        }

        // a condição abaixo é útil, pois várias consultas com as mesmas variáveis 'query' e 'rows' sempre retornam o mesmo resultado na função 'getColumnsValues()', a não ser que 'reloadQuery()' seja executado, e posteriormente 'getColumnsValues()';
        if (!$this->fetch_assoc_ja_executado_para_nova_query) {

            if ($this->rows == '1') {
                $this->fetchAssocSingleRow();
                return $this->resp_single_row;
            } else if ($this->rows == '*') {
                $this->fetchAssocMultipleRows();
                return $this->resp_multiple_rows;
            }
        } else {
            if ($this->rows == '1') {
                return $this->resp_single_row;
            } else if ($this->rows == '*') {
                return $this->resp_multiple_rows;
            }
        }
    }

    public function getQueryRows() {
        return mysql_num_rows($this->query);
    }

    protected function fetchAssocSingleRow() {
        if ($this->getQueryRows() == 0) {
            $this->resp_single_row = NULL;
            return;
        }

        $this->row = $this->fetch($this->query);

        if ($this->select_all_columns) {
            $this->resp_single_row = array();
            foreach ($this->row as $k => $v) {
                $this->resp_single_row[$k] = $v;
            }
        } else if ($this->columns_names_type == 'string') {
            $this->resp_single_row = $this->row[$this->columns_names];
        } else {
            // array
            $this->resp_single_row = array();
            foreach ($this->columns_names as $v) {
                array_push($this->resp_single_row, $this->row[$v]);
                //array_push($this->resp_single_row, $v);
                //echo $this->row[$v];
            }
        }
        $this->fetch_assoc_ja_executado_para_nova_query = true;
    }

    protected function fetchAssocMultipleRows() {
        if ($this->getQueryRows() == 0) {
            $this->resp_multiple_rows = NULL;
            return;
        }
        $provisoria = NULL;
        $this->resp_multiple_rows = array();
        //echo $this->showQuery();
        //var_dump($this->resp_multiple_rows);
        while ($this->row = $this->fetch($this->query)) {
            //var_dump($this->row);

            if ($this->select_all_columns) {
                $provisoria = array();
                foreach ($this->row as $k => $v) {
                    $this->provisoria[$k] = $v;
                }
                array_push($this->resp_multiple_rows, $this->provisoria);
            } else if ($this->columns_names_type == 'array') {
                $provisoria = array();
                for ($i = 0; $i < sizeof($this->columns_names); $i++) {
                    array_push($provisoria, $this->row[$this->columns_names[$i]]);
                }
                array_push($this->resp_multiple_rows, $provisoria);
            } else {
                // string
                //echo $this->columns_names;
                array_push($this->resp_multiple_rows, $this->row[$this->columns_names]);
            }
        }
        $this->fetch_assoc_ja_executado_para_nova_query = true;
    }

    protected function fetch($query) {
        if ($this->fetch_row) {
            //echo "fetch_row: " . $query . ";";
            return mysql_fetch_row($this->query);
        } else {
            //echo "fetch_assoc: " . $query . ";";
            return mysql_fetch_assoc($this->query);
        }
    }

    public function fetchRow($bool) {
        $this->fetch_row = $bool;
    }

    protected function verificaNome($tipo, $nome) {
        //echo $tipo . $nome;
        switch ($tipo) {
            case 'db':
            case 'table':
                $this->regex_pattern = '/^[^/\.]+$/';
                break;
            case 'column':
                $this->regex_pattern = '/^.+$/';
                break;
            default:
                return false;
        }
        if (!preg_match($this->regex_pattern, $nome))
            throw new DBException("nome do BD, tabela ou coluna está incorreto. parâmetros: tipo = '" . $tipo . "', nome = '" . $nome . "';");
    }

    public function closeConnection() {
        mysql_close($this->resource);
    }

    public function lastInsertID() {
        return mysql_insert_id($this->resource);
    }

    public function setPefixoTodasTabelas($prefixo) {
        $this->prefixo_todas_tabelas = $prefixo;
    }
}




/* EXCEÇÕES */


/**
 * não pode remover ou adicionar registro 
 * porque é chave estrangeira em outra tabela: 
 */
class DBM_ForeignKeyConstraintException extends DBM_Exception { }


/**
 * chave duplicada (Primary key ou Unique Key):
 */
class DBM_DuplicateKeyException extends DBM_Exception { }


/**
 * Erro geral de conexão ou consulta, lançado pelo MySQL:
 */
class DBM_Exception extends Exception { }

?>